//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
 /******************************************************************************
**
** INTEL CONFIDENTIAL
** Copyright 2003-2005 Intel Corporation All Rights Reserved.
**
** The source code contained or described herein and all documents
** related to the source code (Material) are owned by Intel Corporation
** or its suppliers or licensors.  Title to the Material remains with
** Intel Corporation or its suppliers and licensors. The Material contains
** trade secrets and proprietary and confidential information of Intel
** or its suppliers and licensors. The Material is protected by worldwide
** copyright and trade secret laws and treaty provisions. No part of the
** Material may be used, copied, reproduced, modified, published, uploaded,
** posted, transmitted, distributed, or disclosed in any way without Intel's
** prior express written permission.
**
** No license under any patent, copyright, trade secret or other intellectual
** property right is granted to or conferred upon you by disclosure or
** delivery of the Materials, either expressly, by implication, inducement,
** estoppel or otherwise. Any license under such intellectual property rights
** must be express and approved by Intel in writing.
**
**  FILENAME: DumbBatt.c
**
**  PURPOSE:  Driver for monitoring a device with a "dumb" battery
**
******************************************************************************/
#include <windows.h>
#include <ceddk.h>
#include <ddkreg.h>

#include <battimpl.h>
#include <cmthread.h>
#include <args.h>
#include <ioctl_cfg.h>

#include "arava.h"
#include "DumbBatt.h"
#include "battery_pdd.h"

#ifdef DEBUG
	#define DEBUGMASK(bit)      (1 << (bit))

	#undef ZONE_ERROR
	#undef ZONE_WARN
	#undef ZONE_INIT
	#undef ZONE_FUNCTION

	// These correspond to the definitions in batzones.c
	//
	#define ZONE_ERROR          DEBUGMASK(0)
	#define ZONE_WARN           DEBUGMASK(1)
	#define ZONE_INIT           DEBUGMASK(2)
	#define ZONE_FUNCTION       DEBUGMASK(7)
	#define ZONE_BATDRV			DEBUGMASK(9)
#endif



//*********************************************************************
//*********************************************************************
//
//					Arrava_PMIC implementation       
//
//---------------------------------------------------------------------
//

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
Arrava_PMIC::Arrava_PMIC() 
		: m_hPMIC(NULL)
{
	// open the Arava driver
	//
	m_hPMIC = CreateFile(PIC_FILE_NAME, 0, 0, NULL, 0, 0, NULL);
	if( m_hPMIC == NULL )
    {
		NKDbgPrintfW(_T("ERROR: can't open PIC(Arava) driver.\r\n"));
	}
	else
	{
		NKDbgPrintfW(_T("Arrava Driver opened.\r\n"));
	}
};

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
Arrava_PMIC::~Arrava_PMIC() 
{
};

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL Arrava_PMIC::Initialize()
{
	if (m_hPMIC == NULL)
		return FALSE;

	if (!(
		Write(PMIC_ADC_AUTO_CTRL, 0x7F ) &&		// Enable Automatic measurements 
		Write(PMIC_ADC_MAN_CTRL, 0x10)			// LDO_INT_ENABLE			
		))
	{
		NKDbgPrintfW(_T("Failed to Initialize Arrava HW.\r\n"));
		return FALSE;
	}
	return TRUE;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
HANDLE Arrava_PMIC::InitializeEvent(DWORD dwEvent)
{
	HANDLE hEvent = NULL;

    DeviceIoControl(m_hPMIC, PIC_IO_GET_EVENT_HANDLE,&dwEvent,sizeof(DWORD), &hEvent, sizeof(DWORD), NULL,NULL);
    if( NULL == hEvent )
    {
        DEBUGMSG(ZONE_ERROR, (_T("ERROR: can't get PMIC Event   Last Error %d.\r\n"),GetLastError() ));
        return NULL;
    }
    if(!DeviceIoControl(m_hPMIC, PIC_IO_ENABLE_EVENT,&dwEvent,sizeof(DWORD), NULL,0, NULL,NULL))
    {
        DEBUGMSG(ZONE_ERROR, (_T("ERROR: can't Enable PMIC Event   Last Error %d.\r\n"),GetLastError() ));
        return NULL;
    }   
	return hEvent;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL Arrava_PMIC::Lock()
{
	if(!DeviceIoControl(m_hPMIC, PIC_IO_LOCK, 0, sizeof(BYTE), 0, sizeof(BYTE),NULL,NULL))
    {
    	NKDbgPrintfW(_T("ERROR:Get Arava lock failed!\r\n"));
    	return FALSE;	
    }
	return TRUE;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL Arrava_PMIC::Unlock()
{
	if(!DeviceIoControl(m_hPMIC, PIC_IO_UNLOCK, 0, sizeof(BYTE), 0, sizeof(BYTE),NULL,NULL))
    {
    	NKDbgPrintfW(_T("ERROR:Get Arava lock failed!\r\n"));
    	return FALSE;	
    }
	return TRUE;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL Arrava_PMIC::Read(BYTE Reg, BYTE *pResult)
{
    BYTE regs[2];
	regs[0] = Reg;

	if(!m_hPMIC)
		return FALSE;

	if(!DeviceIoControl(m_hPMIC, PIC_IO_GET_REG, regs,sizeof(BYTE), regs+1, sizeof(BYTE),NULL,NULL))
    {
    	NKDbgPrintfW(_T("ERROR:Get Arava status register failed!\r\n"));
    	return FALSE;	
    }

	*pResult = regs[1];
	return TRUE;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL Arrava_PMIC::Write(BYTE Reg, BYTE Val)
{
    BYTE regs[2];
	regs[0] = Reg;
	regs[1] = Val;

	if(!DeviceIoControl(m_hPMIC, PIC_IO_SET_REG, regs, sizeof(BYTE)*2, NULL,NULL,NULL,NULL))
    {
    	NKDbgPrintfW(_T("ERROR:Get Arava status register failed!\r\n"));
    	return FALSE;	
    }

	return TRUE;
}


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
void Arrava_PMIC::CCTR_Timer_set(DWORD minutes)
{
	// Register value represents the number of 8 minute intervals to charge.

	BYTE reg = (BYTE)(minutes / 8);
	Write(PMIC_BATTERY_CCTR_CTRL, reg);
}


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
void Arrava_PMIC::TCTR_Timer_set(DWORD hours)
{
	// Register value represents the number of hours to charge - up to a 
	// maximum of 15 hours.
	//
	BYTE reg = (BYTE)(hours);
	Write(PMIC_BATTERY_TCTR_CTRL, reg);
}



//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
void Arrava_PMIC::Set_VBAT_high_limit(DWORD mV)
{
	BYTE reg = VBAT_mV_2_ADC(mV);
	DEBUGMSG(ZONE_BATDRV, (_T("Setting VBAT_high: %d (%d mV)\r\n"), reg, mV));

	Write(PMIC_ADC_VBATMON, reg);
	Write(PMIC_ADC_VBATMONTXON, reg);
}

BOOL Arrava_PMIC::IsBatteryDetected(DWORD VBAT_mV)
{
	 return ( VBAT_mV < (BATTERY_VOLTAGE_MAX + 5)); 
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
DWORD Arrava_PMIC::VBAT_ADC_2_mV(BYTE ADC_VBAT, BOOL bACChargerDetected, BOOL bUSBChargerDetected)
{	
// The VBAT to mV conversion is derived from the graphs on page 46 of the 
// arava data sheet, and have been tweaked experimentally to match voltage 
// as measured at the battery.
// These may not be very precise but should be in the ballpark.
//
	if (bACChargerDetected)
	{
		return ADC_VBAT + 260; 
	}
	else if (bUSBChargerDetected)
	{
		return ADC_VBAT + 272;
	}
	else
	{
		return ADC_VBAT + 278;
	}
}


BYTE Arrava_PMIC::VBAT_mV_2_ADC(DWORD VBAT)
{
	return (BYTE)(VBAT - 278); 
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
void Arrava_PMIC::Shutdown()
{
	if(m_hPMIC)
	{
		CloseHandle(m_hPMIC);
		m_hPMIC = NULL;	
	}
};


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BYTE Arrava_PMIC::GetVBAT() 
{ 	
	BYTE VBAT = 0;	
	Read(PMIC_ADC_VBAT_RES, &VBAT); 
	return VBAT;
};

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BYTE Arrava_PMIC::GetTBAT()
{
	BYTE TBAT;
	Read(PMIC_ADC_TBAT_RES, &TBAT);
	return TBAT;
};

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BYTE Arrava_PMIC::GetICHG() 
{ 	
	// Manual readout seems to be much more reliable for current.
	//
	BYTE ICHG = 0;

	Lock();
	Write(PMIC_ADC_MAN_CTRL, 0x11);
	Write(PMIC_ADC_MAN_CTRL, 0x19);
	Read(PMIC_ADC_MAN_RES, &ICHG);
	Unlock();

	return ICHG;
};

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BYTE Arrava_PMIC::GetVCHG()
{
	BYTE Temp = -1;

	if(!m_hPMIC)
	{
		return -1;
	}

	Lock();
	Write(PMIC_ADC_MAN_CTRL, 0x12);
	Write(PMIC_ADC_MAN_CTRL, 0x1A);
	Read(PMIC_ADC_MAN_RES, &Temp);
	Unlock();

	return Temp;
}



//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
//  Check the arava status register to see if a Battery Charger is
// detected.
//
BOOL Arrava_PMIC::IsChargerDetected(BOOL *pACCharger, BOOL *pUSBCharger)
{
	BYTE Status = 0;
	Read(PMIC_STATUS, &Status);


	*pACCharger		= ((Status & 0x08) != 0); // Check Bit 4
	*pUSBCharger	= ((Status & 0x04) != 0); // Check bit 3

	DEBUGMSG(ZONE_BATDRV, (_T("Status Reg: 0x%x\r\n"), Status));


	DEBUGMSG(ZONE_BATDRV, (_T("AC Charger %s detected!\r\n"),
						(*pACCharger ? _T("is") : _T("is *NOT*")) ));

	DEBUGMSG(ZONE_BATDRV, (_T("USB Charger %s detected!\r\n"),
						(*pUSBCharger ? _T("is") : _T("is *NOT*")) ));

	// When both AC and USB are active and the AC becomes inactive the PMIC
	// doesn't report the change in the status bit.  This workaround looks at 
	// the charger voltage level to determine when it has dropped off.
	//
	BYTE uVCHG = GetVCHG();
	if (*pACCharger && *pUSBCharger && (uVCHG < 75))
	{
		DEBUGMSG(ZONE_BATDRV, (_T("Modifing AC Charger Status.\r\n") ));

		*pACCharger = FALSE;
	}
	return (*pUSBCharger || *pACCharger);
}



//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
//  Turn the charger on or off and set the current and voltage parameters.
//
void Arrava_PMIC::SetCharger(BOOL Enabled, DWORD dwCurrent, DWORD dwVoltage)
{
	DEBUGMSG(ZONE_INIT, (_T("DumbBatDrv::BatteryCharger::Enable() %d %d\r\n"), dwCurrent, dwVoltage));

	BYTE reg = 0;

	if (Enabled)
	{
		reg = (CHARGER_ENABLE | ((BYTE)(dwCurrent / 100) << 3) | (BYTE)dwVoltage);
	}
	else
	{
		reg = CHARGER_DISABLE;
	}

	Write(PMIC_BATTERY_CHARGER_CTRL, reg);

	RETAILMSG(1, (_T("Battery charger is %s! - %d\r\n"), (Enabled ? _T("ON") : _T("OFF"))));

}

//
//---------------------------------------------------------------------
//
//		End Arrava_PMIC implementation       
//
//*********************************************************************
//*********************************************************************




//*********************************************************************
//*********************************************************************
//
//		DumbBatDrv implementation       
//
//---------------------------------------------------------------------
//

DumbBatDrv DumbBatDrv::m_Instance;


//=====================================================================
//
//		Private Methods
//
//---------------------------------------------------------------------
//
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
DumbBatDrv::DumbBatDrv()
: CMiniThread()
, m_hEvent_CHDET(NULL)
, m_hEvent_VBUS_DETECT(NULL)
, m_uVBAT(0)
, m_uTBAT(0)
, m_uICHG(0)
, m_bChargerEnabled(FALSE)
, m_bACChargerDetected(FALSE)
, m_uBatteryLifePercent(0)
, m_dwChargerVoltage(CHARGER_VOLTAGE_4_2V)
, m_dwChargerCurrent(100)
, m_bCharging(FALSE)

#ifdef DEBUG
, m_uVCHG(0)
, m_ChargeMode(CHARGE_MODE_OFF)
#endif
{
	// This table matches voltages to percent remaining values.
	// This is a rough approximation of the values.  It needs
	// to be tweaked after some experimentation.
	//
	m_BatteryVoltageTable[0] = 320; // 0% voltage value		
	m_BatteryVoltageTable[1] = 332; // 10% voltage value	+12
	m_BatteryVoltageTable[2] = 342; // 20% voltage value	+10
	m_BatteryVoltageTable[3] = 350; // 30% voltage value	+8
	m_BatteryVoltageTable[4] = 355; // 40% voltage value	+5
	m_BatteryVoltageTable[5] = 360; // 50% voltage value	+5

	m_BatteryVoltageTable[6] = 365; // 60% voltage value	+5
	m_BatteryVoltageTable[7] = 370; // 70% voltage value	+5
	m_BatteryVoltageTable[8] = 377; // 80% voltage value	+7
	m_BatteryVoltageTable[9] = 387; // 90% voltage value	+10

	m_BatteryVoltageTable[10]= 400; // 100%					+12
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
DumbBatDrv::~DumbBatDrv()
{
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
void DumbBatDrv::GetBatteryPercent (DWORD batteryLevel)
{
    BYTE  batteryPercent;
    DWORD  lowval, hival;
    int   index;

    // Look in table and find the highest percentage lower than the current value
	//
    index = 10;
    while ((batteryLevel < m_BatteryVoltageTable[index])&& (index > 0))
    {
        index--;
    }

    // If index >= 10, we would read off the end of the array, so just return full...
	//
    if(index >= 10)
    {
        batteryPercent = 100;
    }
    else
    {
        // Figure out the percentage by linear interpolation between
        //  the low and hi points for the current
		//
        lowval = m_BatteryVoltageTable[index];
        hival =  m_BatteryVoltageTable[index+1];
        
        // If the battery level goes below the lowest value in the table,
        // some of the values in this calculation go negative.  This compensates.
        if(batteryLevel < lowval)
        {
            batteryPercent = 0;
        }
        else
        {
            batteryPercent = (BYTE)(
									(10 * index) 
									+ ( (10 * (batteryLevel - lowval) + (hival-lowval)/2) 
									/ (hival-lowval) ) 
									);

			DEBUGMSG(ZONE_BATDRV, (_T("Calculated batteryPercent: %d \r\n"), batteryPercent));
		}
    }

	m_uBatteryLifePercent = batteryPercent;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
DWORD DumbBatDrv::ThreadRun()
{   
	RETAILMSG(1, (_T("Battery: Starting Thread\r\n")));

	if (m_bBatteryDetected)
	{
		while(1)
		{  
			HANDLE events[]=
			{
				m_hEvent_CHDET
				, m_hEvent_VBUS_DETECT
			};

			DWORD object=WaitForMultipleObjects(2, events, FALSE, DB_OUTPUT_TIME);

			switch(object)
			{
				case WAIT_OBJECT_0: // ChargerDetect Event
				{
					RETAILMSG(1, (_T("Battery Event: ChargerDetect\r\n")));
					
					PMIC.IsChargerDetected(&m_bACChargerDetected, &m_bUSBChargerDetected);

					RETAILMSG(1, (_T("AC Charger %s detected!  -  USB Charger %s detected!\r\n"),
										(m_bACChargerDetected ? _T("is") : _T("is *NOT*")),
										(m_bUSBChargerDetected ? _T("is") : _T("is *NOT*"))
										));

					ConfigureCharger();
					break;
				}
				case WAIT_OBJECT_0+1: // (USB)VBUS Detect 
				{
					RETAILMSG(1, (_T("Battery Event: USB VBUS Detect\r\n")));

					PMIC.IsChargerDetected(&m_bACChargerDetected, &m_bUSBChargerDetected);

					RETAILMSG(1, (_T("AC Charger %s detected!  -  USB Charger %s detected!\r\n"),
										(m_bACChargerDetected ? _T("is") : _T("is *NOT*")),
										(m_bUSBChargerDetected ? _T("is") : _T("is *NOT*"))
										));

					ConfigureCharger();
					break;
				}
				case WAIT_TIMEOUT:
				{
                    SYSTEMTIME SystemTime;
                    GetSystemTime(&SystemTime);

                    RETAILMSG(1, (_T("Battery Status - VBAT: %d (~%d mV), ICHG: %d,  Charger(s): %s%s%s,  Time: %d:%02d,  Charge remaining: ~%d%%\r\n"), 
						m_uVBAT, 
						m_dwVBAT_mV, 
						m_uICHG,
						((m_bACChargerDetected || m_bUSBChargerDetected) ? _T("") : _T("None")),
						(m_bACChargerDetected ? _T("AC ") : _T("")),
						(m_bUSBChargerDetected ? _T("USB") : _T("")),
                        SystemTime.wHour, SystemTime.wMinute,
						m_uBatteryLifePercent
						));
						
					break;
				}

				default:
				{
					RETAILMSG(1, (_T("Battery Event: Wait object Error\r\n")));
					break;
				}
			} // switch
		} // while (1)
	}

	RETAILMSG(1, (_T("Battery: Exiting Thread - driver not enabled in EBOOT.\r\n")));

	return 0;
} 


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
#ifdef DEBUG
void DumbBatDrv::ShowStatus()
{
DEBUGMSG(ZONE_BATDRV, (_T("\r\n ----- Battery Update -----\r\n")));

DEBUGMSG(ZONE_BATDRV, (_T("\r\nPMIC Status:\r\n\
	VBAT: %d (%d mV)\r\n\
    TBAT: %d\r\n\
    ICHG: %d\r\n\
    VCHG: %d\r\n"),

	m_uVBAT,
	m_dwVBAT_mV,
	m_uTBAT,
	m_uICHG,
	m_uVCHG
	));

DEBUGMSG(ZONE_BATDRV, (_T("\r\n")));

DEBUGMSG(ZONE_BATDRV, (_T("\r\nBattery Status:\r\n\
    AC Charger Detected: %s\r\n\
    USB ChargerDetected: %s\r\n\
    ChargerEnabled: %s  (I/V: %d mA/%d mV)\r\n\
    Charging: %s\r\n\
	Charge_Mode: %s\r\n\
    Remaining Percent: %d\r\n\r\n"),

	(m_bACChargerDetected ? _T("Yes") : _T("No")),
	(m_bUSBChargerDetected ? _T("Yes") : _T("No")),
	(m_bChargerEnabled ? _T("Yes") : _T("No")),	m_dwChargerCurrent,	400 + (m_dwChargerVoltage * 5),
	(m_bCharging ? _T("Yes") : _T("No")),
	(m_ChargeMode == CHARGE_MODE_CC ? _T("CC") : m_ChargeMode == CHARGE_MODE_TC ? _T("TC") : _T("OFF")),
	m_uBatteryLifePercent
	));
}
#endif



//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
void DumbBatDrv::ConfigureCharger()
{
	DEBUGMSG(ZONE_BATDRV, (_T("DumbBatDrv::ConfigureCharger()\r\n")));

	if (m_bACChargerDetected)
	{
		m_bChargerEnabled = ON;

		if (m_dwVBAT_mV < BATTERY_VOLTAGE_MIN)
		{
			m_dwChargerCurrent = 100;
			m_dwChargerVoltage = CHARGER_VOLTAGE_4_2V;
		}
		else 
		{
			m_dwChargerCurrent = 700;
			m_dwChargerVoltage = CHARGER_VOLTAGE_4_2V;
		}
	}
	else if (m_bUSBChargerDetected)
	{
		m_bChargerEnabled = ON;

		if (m_dwVBAT_mV < BATTERY_VOLTAGE_MIN)
		{
			m_dwChargerCurrent = 100;
			m_dwChargerVoltage = CHARGER_VOLTAGE_4_2V;
		}
		else 
		{
			m_dwChargerCurrent = 500;
			m_dwChargerVoltage = CHARGER_VOLTAGE_4_2V;
		}
	}
	else if (m_bChargerEnabled) // No Charger detected or no Battery - turn off the charger.
	{
		m_bChargerEnabled = OFF;
		m_dwChargerCurrent = 100;
		m_dwChargerVoltage = CHARGER_VOLTAGE_4_2V;

		PMIC.SetCharger(m_bChargerEnabled, m_dwChargerCurrent, m_dwChargerVoltage);

	}

	if (m_bChargerEnabled)
	{
		PMIC.Set_VBAT_high_limit(BATTERY_VOLTAGE_MAX);

		RETAILMSG(1, (_T("Setting Charger Current to %d mA\r\n"), m_dwChargerCurrent));
		PMIC.SetCharger(m_bChargerEnabled, m_dwChargerCurrent, m_dwChargerVoltage);
	}
}


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
void DumbBatDrv::UpdateBatteryStatus()
{
	DEBUGMSG(ZONE_INIT, (_T("DumbBatDrv::UpdateBatteryStatus()\r\n")));

	// Read the status values from the PMIC
	//
	m_uVBAT = PMIC.GetVBAT();
	m_uTBAT = PMIC.GetTBAT();
	m_uICHG = PMIC.GetICHG();

	m_bCharging = (m_uICHG > 1); //)Is there charger current?  Should be able to use 0 but I've seen 1 here when no battery is attached.

	m_dwVBAT_mV = PMIC.VBAT_ADC_2_mV(m_uVBAT, m_bACChargerDetected, m_bUSBChargerDetected);

	GetBatteryPercent(m_dwVBAT_mV);

#ifdef DEBUG
	m_uVCHG = PMIC.GetVCHG();
	ShowStatus();
#endif
}
//
//---------------------------------------------------------------------
//
//		End Private Methods
//
//=====================================================================




//=====================================================================
//
//		Public Methods
//
// These are the Main Entry Points to Object - these are called directly
// by the DLL entry points below.
//
//---------------------------------------------------------------------


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL DumbBatDrv::Init()
{
	RETAILMSG(1, (_T("\"Dumb\" Battery Driver (%s) initialization. \r\n"), VERSION_STRING));

	m_bBatteryDetected = IsDriverEnabled();
	RETAILMSG(1, (_T("Battery Driver is %s in EBOOT."), (m_bBatteryDetected ? _T("Enabled") : _T("Disabled")) ));

	if(m_bBatteryDetected)
	{
		RETAILMSG(1, (_T("Battery *MUST* be installed for proper operation.") ));
	}
	else
	{
		RETAILMSG(1, (_T("Battery  *NOT* Present - Charger disabled...") ));
        m_uBatteryLifePercent = 50; // return a fake value.
        m_bCharging = TRUE; // AC is connected..
		return TRUE;
	}

	// Initialize the Interrupt events
	//
	m_hEvent_CHDET				= PMIC.InitializeEvent(PIC_EVENT_CHDET);
	m_hEvent_VBUS_DETECT		= PMIC.InitializeEvent(PIC_EVENT_USB1_VBUS_DETECT);

	PMIC.Initialize();

	// Find out if the charger was turned on by IPL
	//
	BYTE reg = 0;
	PMIC.Read(PMIC_BATTERY_CHARGER_CTRL, &reg);
	m_bChargerEnabled = ((reg & 0x80) ? ON : OFF);
	m_dwChargerCurrent = ((reg >> 3)& 0x0F) * 100;
	
	RETAILMSG(1, (_T("Charger %s already enabled by bootloader - %d mA.\r\n"), 
		(m_bChargerEnabled ? _T("is") : _T("is NOT")),
		m_dwChargerCurrent
		));
	
	PMIC.IsChargerDetected(&m_bACChargerDetected, &m_bUSBChargerDetected);
	UpdateBatteryStatus();

	RETAILMSG(1, (_T("Battery - VBAT: %d (~%d mV) Status: %d%%.\r\n"), 
		m_uVBAT, 
		m_dwVBAT_mV, 
		m_uBatteryLifePercent
		));

	RETAILMSG(1, (_T("Charger(s) Detected: %s%s%s\r\n"), 
		((m_bACChargerDetected || m_bUSBChargerDetected) ? _T("") : _T("None")),
		(m_bACChargerDetected ? _T("AC ") : _T("")),
		(m_bUSBChargerDetected ? _T("USB") : _T(""))
		));

	if (!m_bChargerEnabled && (m_bACChargerDetected || m_bUSBChargerDetected) )	
	{
		ConfigureCharger();
	}

	ThreadStart();

	return TRUE;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL DumbBatDrv::BatterySleepModeEnable()
{
	return TRUE;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL DumbBatDrv::IsDriverEnabled()
{
	 BSP_ARGS args;
	 DWORD size;

	 if(!KernelIoControl(IOCTL_GET_BSP_ARGS,NULL,0,&args,sizeof(args),&size))
	 {
		RETAILMSG(1, (_T("Battery Driver: IOCTL_GET_BSP_ARGS failed!") ));
		return FALSE;
	 }

	return args.BatteryEnable;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL DumbBatDrv::DetectBattery()
{
    if (m_bBatteryDetected)
    {
	    DEBUGMSG(ZONE_BATDRV, (_T(" ----- DetectBattery Called! ----- \r\n")));

	// This is the first routine called by the PDD so we will update status
	// now so that it will be available for the subsequent calls.
	//
	UpdateBatteryStatus();

	if ( ((m_dwChargerCurrent == 700) && (m_dwVBAT_mV >= BATTERY_VOLTAGE_MAX)) ||
		 ((m_dwChargerCurrent == 100) && (m_dwVBAT_mV < BATTERY_VOLTAGE_MAX - 10))
		)	
	    {
		    ConfigureCharger();
	    }
    }

	// We can't reliably detect whether a battery is available on Littleton.
	// So we will just return true.
	// 
	return TRUE;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL DumbBatDrv::GetBatteryLifePercent(BYTE *pLifePercent)
{
	*pLifePercent = m_uBatteryLifePercent;
	return TRUE;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL DumbBatDrv::GetBatteryVoltage(DWORD *pVoltage)
{
	*pVoltage = m_dwVBAT_mV;

	return TRUE;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL DumbBatDrv::GetBatteryCurrent(DWORD *pCurrent)
{
	*pCurrent = 0;

	return TRUE;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL DumbBatDrv::GetBatteryTemperature(DWORD *pTemperature)
{
	*pTemperature = m_uTBAT;
	return TRUE;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
BOOL DumbBatDrv::CheckChargerState()
{
	return m_bCharging;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
void DumbBatDrv::DeInit()
{
	RETAILMSG(1, (_T(" ----- Battery Driver DeInit() Called! ----- \r\n")));
	PMIC.Shutdown();
}
//
//---------------------------------------------------------------------
//
//		End public methods
//
//=====================================================================
//
//---------------------------------------------------------------------
//
//		End	DumbBatDrv implementation       
//
//*********************************************************************







//*********************************************************************
//
//	DLL External Entry Points
//
//---------------------------------------------------------------------
// Function pointers that will be accessed and called from battery_pdd.c
//
OEM_BATTERYROUTINES gOEMBatteryRoutines = {
	BatDrv_Init,
	BatDrv_BatterySleepModeEnable,	
	BatDrv_DetectBattery,
	BatDrv_GetBatteryLifePercent,
	BatDrv_GetBatteryVoltage,
	BatDrv_GetBatteryCurrent,
	BatDrv_GetBatteryTemperature,
	BatDrv_CheckChargerState,
	BatDrv_DeInit
};

//---------------------------------------------------------------------
// External Entry Points - these  map to the coresponding object method.
//
extern "C" BOOL BatDrv_Init() 
{	
	return DumbBatDrv::GetInstance().Init(); 
}

extern "C" BOOL BatDrv_BatterySleepModeEnable()
{	
	return DumbBatDrv::GetInstance().BatterySleepModeEnable(); 
}

extern "C" BOOL BatDrv_DetectBattery()
{
	return DumbBatDrv::GetInstance().DetectBattery();
}
extern "C" BOOL BatDrv_GetBatteryLifePercent(BYTE *pLifePercent)
{
	return DumbBatDrv::GetInstance().GetBatteryLifePercent(pLifePercent);
}

extern "C" BOOL BatDrv_GetBatteryVoltage(DWORD *pVoltage)
{
	return DumbBatDrv::GetInstance().GetBatteryVoltage(pVoltage);
}

extern "C" BOOL BatDrv_GetBatteryCurrent(DWORD *pCurrent)
{
	return DumbBatDrv::GetInstance().GetBatteryCurrent(pCurrent);
}

extern "C" BOOL BatDrv_GetBatteryTemperature(DWORD *pTemperature)
{
	return DumbBatDrv::GetInstance().GetBatteryTemperature(pTemperature);
}

extern "C" BOOL BatDrv_CheckChargerState()
{
	return DumbBatDrv::GetInstance().CheckChargerState();
}

extern "C" void BatDrv_DeInit()
{
	DumbBatDrv::GetInstance().DeInit();
}

//---------------------------------------------------------------------
//
//		End DLL External Entry Points
//
//*********************************************************************


